#include "d3d9/d3d9display.h"
#include "d3d9/d3d9mesh.h"

#include "Mesh.h"

using namespace Kraw;

#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")

////////////////////////////////////////////////////////////////////////////////
///	D3D9 Display
IDirect3D9* g_pD3D9 = NULL;

int D3D9Display::numDisplays = 0;

#if 0 //test drawing
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; // Buffer to hold vertices
struct CUSTOMVERTEX
{
    FLOAT x, y, z;      // The untransformed, 3D position for the vertex
    DWORD color;        // The vertex color
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)
#endif

D3D9Display::D3D9Display() :
	pD3D9Device(NULL),
	pCurShader(NULL)
{
	width = 640;
	height = 480;
	left = (GetSystemMetrics(SM_CXSCREEN) - width) >> 1;
	top = (GetSystemMetrics(SM_CYSCREEN) - height) >> 1;

    memset(&(this->presentParams), 0, sizeof(this->presentParams));
}

////////////////////////////////////////////////////////////////////////////////
///	D3D9 Implementation
Display* Display::Create()
{
    if (!g_pD3D9)
    {
		g_pD3D9 = Direct3DCreate9( D3D_SDK_VERSION );
		if (g_pD3D9 == NULL)
			return 0;
    }
    else
        g_pD3D9->AddRef();

	D3D9Display *self = new D3D9Display();

	return self;
}

/// D3D9 implementation
D3D9Display::~D3D9Display()
{
    Close();

    g_pD3D9->Release();
}

/// Open display
bool Display::Open()
{
	Close();

	D3D9Display* self = (D3D9Display*) this;

	if (!self->OpenWindow())
		return false;

	//TODO- expose creation parameters through callbacks
    ZeroMemory( &self->presentParams, sizeof(self->presentParams) );
    self->presentParams.Windowed = TRUE;
    self->presentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
    self->presentParams.BackBufferFormat = D3DFMT_UNKNOWN;

    if( FAILED( g_pD3D9->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, self->hWnd,
                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                      &self->presentParams, &self->pD3D9Device ) ) )
    {
        return false;
    }

    //TEMP- basic render states
    //self->pD3D9Device->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
    self->pD3D9Device->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
    self->pD3D9Device->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
    self->pD3D9Device->SetRenderState( D3DRS_LIGHTING, FALSE );

	//default transforms
	self->InitTransforms();

    ShowWindow( self->hWnd, SW_SHOWDEFAULT );
    UpdateWindow( self->hWnd );
    
#if 0 //test drawing
    // Initialize three vertices for rendering a triangle
    CUSTOMVERTEX g_Vertices[] =
    {
        { -1.0f,-1.0f, 0.0f, 0xffff0000, },
        {  1.0f,-1.0f, 0.0f, 0xff0000ff, },
        {  0.0f, 1.0f, 0.0f, 0xffffffff, },
    };

    // Create the vertex buffer. Here we are allocating enough memory
    // (from the default pool) to hold all our 3 custom vertices. We also
    // specify the FVF, so the vertex buffer knows what data it contains.
    if( FAILED( self->pD3D9Device->CreateVertexBuffer( 3 * sizeof( CUSTOMVERTEX ),
                                                  0, D3DFVF_CUSTOMVERTEX,
                                                  D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
    {
        return false;
    }

    // Now we fill the vertex buffer. To do this, we need to Lock() the VB to
    // gain access to the vertices. This mechanism is required becuase vertex
    // buffers may be in device memory.
    VOID* pVertices;
    if( FAILED( g_pVB->Lock( 0, sizeof( g_Vertices ), ( void** )&pVertices, 0 ) ) )
        return false;
    memcpy( pVertices, g_Vertices, sizeof( g_Vertices ) );
    g_pVB->Unlock();
#endif

	bIsOpen = true;
	return bIsOpen;
}

/// Close display
void Display::Close()
{
	if (bIsOpen)
	{
#if 0 //test drawing
		if( g_pVB != NULL )
			g_pVB->Release();
#endif
		D3D9Display* self = (D3D9Display*) this;
		if (self->pD3D9Device)
		{
		    self->pD3D9Device->Release();
		    self->pD3D9Device = 0;
		}

		bIsOpen = false;
	}
}

/// Update display - message loop
bool Display::Update()
{
    D3D9Display* self = (D3D9Display*) this;

    MSG msg;
    ZeroMemory( &msg, sizeof( msg ) );
    if ( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
    {
		TranslateMessage( &msg );
		DispatchMessage( &msg );
	}
	
	if ( msg.message == WM_QUIT )
	{
		self->bIsQuitRequested = true;
	}
    
    return !self->bIsQuitRequested;
}

/// begin render
bool Display::BeginDraw()
{
    D3D9Display* self = (D3D9Display*) this;
    if (self->pD3D9Device)
    {
        self->pD3D9Device->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0, 0, 255 ), 1.0f, 0 );
        
        if (SUCCEEDED( self->pD3D9Device->BeginScene() ) )
            return true;
    }
    
    return false;
}

/// end render
void Display::EndDraw()
{
    D3D9Display* self = (D3D9Display*) this;

    if (self->pD3D9Device)
    {
        // End the scene
        self->pD3D9Device->EndScene();

        // Present the backbuffer contents to the display
        self->pD3D9Device->Present( NULL, NULL, NULL, NULL );
    }
}

/// clear buffer(s)
void Display::Clear(int bufferTypes, float red, float green, float blue, float alpha, float z, int stencil)
{
    D3D9Display* self = (D3D9Display*) this;

    DWORD flags = 0;
    if (bufferTypes & ColorBuffer)
    {
        flags |= D3DCLEAR_TARGET;
    }
    if (bufferTypes & DepthBuffer)
    {
        flags |= D3DCLEAR_ZBUFFER;
    }
    if ((bufferTypes & StencilBuffer) && 
        ((self->presentParams.AutoDepthStencilFormat == D3DFMT_D24S8) ||
         (self->presentParams.AutoDepthStencilFormat == D3DFMT_D24X4S4)))
    {
        flags |= D3DCLEAR_STENCIL;
    }

    DWORD d3dColor = D3DCOLOR_COLORVALUE(red, green, blue, alpha);

    // no stencil buffer
    self->pD3D9Device->Clear(0, NULL, flags, d3dColor, z, stencil);
}

/// test rendering
void Display::TestDraw()
{
    D3D9Display* self = (D3D9Display*) this;

#if 0 //test drawing
    // Render the vertex buffer contents
    self->pD3D9Device->SetStreamSource( 0, g_pVB, 0, sizeof( CUSTOMVERTEX ) );
    self->pD3D9Device->SetFVF( D3DFVF_CUSTOMVERTEX );
    self->pD3D9Device->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 1 );
#endif
}
